package protocol

import (
	"bytes"
	"encoding/binary"
	"io"

	"github.com/pkg/errors"
)

type defaultProtocol struct {
}

func NewDefaultProtocol() Protocol {
	return &defaultProtocol{}
}

type packet struct {
	key       uint16
	id        uint64
	eventSize uint32
	size      uint32
	event     []byte
	body      []byte
}

func (dp *defaultProtocol) NewPacket(key MessageKey, id uint64, event string, data []byte) Packet {
	return &packet{
		key:       key,
		id:        id,
		eventSize: uint32(len(event)),
		size:      uint32(len(data)),
		event:     []byte(event),
		body:      data,
	}
}

func (p *packet) NewReplyPacket(data []byte) Packet {
	return &packet{
		key:       MessageKeyAckResponse,
		id:        p.id,
		eventSize: 0,
		size:      uint32(len(data)),
		body:      data,
	}
}

func (p *packet) Key() uint16 {
	return p.key
}

func (p *packet) Id() uint64 {
	return p.id
}

func (p *packet) Event() string {
	return string(p.event)
}

func (p *packet) Body() []byte {
	return p.body
}

func (dp *defaultProtocol) WritePacket(pack Packet, w io.Writer) (int, error) {
	p := pack.(*packet)
	eventSize := len(p.event)
	size := len(p.body)
	buf := make([]byte, 18)
	buffer := bytes.NewBuffer([]byte{})
	binary.BigEndian.PutUint16(buf[:2], p.key)
	binary.BigEndian.PutUint64(buf[2:10], p.id)
	binary.BigEndian.PutUint32(buf[10:14], uint32(eventSize))
	binary.BigEndian.PutUint32(buf[14:18], uint32(size))
	buffer.Write(buf)
	buffer.Write(p.event)
	buffer.Write(p.body)
	return w.Write(buffer.Bytes())
}

func (dp *defaultProtocol) ReadPacket(r io.Reader) (Packet, error) {
	var p packet
	err := binary.Read(r, binary.BigEndian, &p.key)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.BigEndian, &p.id)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.BigEndian, &p.eventSize)
	if err != nil {
		return nil, err
	}
	err = binary.Read(r, binary.BigEndian, &p.size)
	if err != nil {
		return nil, err
	}
	if p.eventSize > 0 {
		buf := make([]byte, p.eventSize)
		_, err = io.ReadFull(r, buf)
		if err != nil {
			return nil, errors.Wrap(err, "read packet event size failed")
		}
		p.event = buf
	}
	if p.size > 0 {
		buf := make([]byte, p.size)
		_, err = io.ReadFull(r, buf)
		if err != nil {
			return nil, errors.Wrap(err, "read packet data size failed")
		}
		p.body = buf
	}
	return &p, nil
}
